package butines.common.math;

public strictfp class Vector2D {
	
	public float x;
	public float y;

	// Constructor
	public Vector2D() {

	}

	public Vector2D(float x, float y) {
		this.x = x;
		this.y = y;
	}

	public Vector2D(Vector2D other) {
		this.x = other.x;
		this.y = other.y;
	}

	public Vector2D copy() {
		return new Vector2D(this);
	}

	// Setter
	public Vector2D set(float x, float y) {
		this.x = x;
		this.y = y;
		return this;
	}

	public Vector2D set(Vector2D other) {
		this.x = other.x;
		this.y = other.y;
		return this;
	}

	// Add
	public Vector2D add(float x, float y) {
		this.x += x;
		this.y += y;
		return this;
	}

	public Vector2D add(Vector2D other) {
		this.x += other.x;
		this.y += other.y;
		return this;
	}
	
    public Vector2D add(Vector2D a, Vector2D b) {
        x = a.x + b.x;
        y = a.y + b.y;
        return this;
    }

	// Sub
	public Vector2D sub(float x, float y) {
		this.x -= x;
		this.y -= y;
		return this;
	}

	public Vector2D sub(Vector2D other) {
		this.x -= other.x;
		this.y -= other.y;
		return this;
	}

    public Vector2D sub(Vector2D a, Vector2D b) {
        x = a.x - b.x;
        y = a.y - b.y;
        return this;
    }

	// Mult
	public Vector2D mult(float x, float y, float value) {
		this.x = x * value;
		this.y = y * value;
		return this;
	}
	
	public Vector2D mult(Vector2D vec, float value) {
		x = vec.x * value;
		y = vec.y * value;
		return this;
	}
	
	public Vector2D mult(float value) {
		x = x * value;
		y = y * value;
		return this;
	}

	// Div
	public Vector2D div(float x, float y, float value) {
		this.x = x / value;
		this.y = y / value;
		return this;
	}

	public Vector2D div(Vector2D vec, float value) {
		x = vec.x / value;
		y = vec.y / value;
		return this;
	}
	
	public Vector2D div(float value) {
		x /= value;
		y /= value;
		return this;
	}

	// Negate
	public Vector2D negate() {
		x = -x;
		y = -y;
		return this;
	}

	public Vector2D negate(Vector2D vec) {
		x = -vec.x;
		y = -vec.y;
		return this;
	}
	
	// Length
	public float lengthSq() {
		return x * x + y * y;
	}

	public float length() {
		return (float) Math.sqrt(x * x + y * y);
	}

	// normalise
	public Vector2D normalise() {
		float norm = 1.0f / length();
		this.x = x * norm;
		this.y = y * norm;
		return this;
	}

	public Vector2D normalise(Vector2D vec) {
		float norm = 1.0f / vec.length();
		this.x = vec.x * norm;
		this.y = vec.y * norm;
		return this;
	}

	// Utils
	public float dot(Vector2D other) {
		return this.x * other.x + this.y * other.y;
	}
	
	public float angle(Vector2D other) {
		float dot = this.dot(other) / (this.length() * other.length());
		if (dot < -1.0f) {
			dot = -1.0f;
		}
		if (dot > 1.0f) {
			dot = 1.0f;
		}
		return (float) Math.acos(dot);
	}

	public float angle() {
		return (float) Math.atan2(y, x);
	}

	public float theta() {
		float theta = (float) Math.toDegrees(Math.atan2(y, x));
		if ((theta < -360) || (theta > 360)) {
			theta = theta % 360;
		}
		if (theta < 0) {
			theta = 360 + theta;
		}
		return theta;
	}

	public Vector2D rotate(float cos, float sin) {
		float newX = x * cos - y * sin;
		float newY = x * sin + y * cos;
		x = newX;
		y = newY;
		return this;
	}
	
    public int sign(Vector2D v2) {
        return (y * v2.x > x * v2.y) ? -1 : 1;
    }
	
    public boolean isZero() {
        return (x * x + y * y) < Float.MIN_VALUE;
    }

    public float distance(Vector2D other) {
        float dx = other.x - this.x;
        float dy = other.y - this.y;
        return (float) Math.sqrt(dx * dx + dy * dy);
    }
    
	@Override
	public String toString() {
		return getClass().getSimpleName() + " {x=" + x + ", y=" + y + ", len=" + length() + ", theta=" + theta() + "}";
	}

	public static Vector2D perpendicular(Vector2D a, Vector2D b) {
	    a.x = -b.y;
	    a.y = b.x;
	    return a;
	}
	
	public static Vector2D truncate(Vector2D vec, float limite) {
		float value = limite / vec.length();
		if (value < 1f) {
			vec.x = vec.x * value;
			vec.y = vec.y * value;
		}
		return vec;
	}
	
	public static Vector2D interpolation(Vector2D v1, Vector2D v2, float blend) {
		v1.x = (v1.x + (v2.x - v1.x) * blend);
		v1.y = (v1.y + (v2.y - v1.y) * blend);
	    return v1;
	}

	public static float distanceSq(Vector2D v1, Vector2D v2) {
		float dx = v2.x - v1.x;
		float dy = v2.y - v1.y;
		return dx * dx + dy * dy;
	}

	public static float distance(Vector2D v1, Vector2D v2) {
		float dx = v2.x - v1.x;
		float dy = v2.y - v1.y;
		return (float) Math.sqrt(dx * dx + dy * dy);
	}
	
	public static Vector2D multAdd(Vector2D c, Vector2D a, float b) {
		c.x = c.x + (a.x * b);
		c.y = c.y + (a.y * b);
		return c;
	}
	
	public static Vector2D add(Vector2D c, Vector2D a, Vector2D b) {
		c.x = a.x + b.x;
		c.y = a.y + b.y;
		return a;
	}
	
	public static Vector2D sub(Vector2D c, Vector2D a, Vector2D b) {
		c.x = a.x - b.x;
		c.y = a.y - b.y;
		return c;
	}
	
}
